/*
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 */
package net.sf.l2j.gameserver.model.actor.instance;

import static net.sf.l2j.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;

import java.util.concurrent.Future;

import javolution.util.FastMap;
import net.sf.l2j.gameserver.ThreadPoolManager;
import net.sf.l2j.gameserver.ai.CtrlIntention;
import net.sf.l2j.gameserver.model.L2Skill;
import net.sf.l2j.gameserver.model.actor.L2Character;
import net.sf.l2j.gameserver.network.serverpackets.NpcInfo;
import net.sf.l2j.gameserver.network.serverpackets.NpcSay;
import net.sf.l2j.gameserver.network.serverpackets.SocialAction;
import net.sf.l2j.gameserver.network.serverpackets.StopMove;
import net.sf.l2j.gameserver.templates.chars.L2NpcTemplate;
import net.sf.l2j.gameserver.templates.skills.L2SkillType;
import net.sf.l2j.util.Rnd;

/** 
 * A tamed beast behaves a lot like a pet and has an owner. Some points :
 * <ul>
 * <li>feeding another beast to level 4 will vanish your actual tamed beast.</li>
 * <li>running out of spices will vanish your actual tamed beast. There's a 1min food check timer.</li>
 * <li>running out of the Beast Farm perimeter will vanish your tamed beast.</li>
 * <li>no need to force attack it, it's a normal monster.</li>
 * </ul>
 * This class handles the running tasks (such as skills use and feed) of the mob.
 */
public final class L2TamedBeastInstance extends L2FeedableBeastInstance
{
	private static final int MAX_DISTANCE_FROM_HOME = 13000;
	private static final int MAX_DISTANCE_FROM_OWNER = 2000;
	private static final int DURATION_CHECK_INTERVAL = 60000;
	private static final int BUFF_INTERVAL = 5000;
	
	private int _foodSkillId;
	private L2PcInstance _owner;
	private Future<?> _buffTask = null;
	private Future<?> _foodTask = null;

	// Messages used every minute by the tamed beast when he automatically eats food.
	protected static final String[] TAMED_TEXT =
	{
		"Refills! Yeah!",
		"I am such a gluttonous beast, it is embarrassing! Ha ha.",
		"Your cooperative feeling has been getting better and better.",
		"I will help you!",
		"The weather is really good. Wanna go for a picnic?",
		"I really like you! This is tasty...",
		"If you do not have to leave this place, then I can help you.",
		"What can I help you with?",
		"I am not here only for food!",
		"Yam, yam, yam, yam, yam!"
	};

    public L2TamedBeastInstance(int objectId, L2NpcTemplate template, L2PcInstance owner, int foodSkillId, int x, int y, int z)
    {
        super(objectId, template);

        disableCoreAI(true); // Make it brainless.
        setCurrentHp(getMaxHp());
        setCurrentMp(getMaxMp());
        
        setOwner(owner);
        setFoodType(foodSkillId);
        
        spawnMe(x, y, z);
    }

    public int getFoodType()
    {
    	return _foodSkillId;
    }

    public void setFoodType(int foodItemId)
    {
    	if (foodItemId > 0)
    	{
        	_foodSkillId = foodItemId;

        	// Cancel the food check, if existing.
        	if (_foodTask != null)
        		_foodTask.cancel(true);
        	
        	// Start the food check.
        	_foodTask = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new FoodCheck(this), DURATION_CHECK_INTERVAL, DURATION_CHECK_INTERVAL);
    	}
    }

    @Override
	public boolean doDie(L2Character killer)
    {
    	if (!super.doDie(killer))
    		return false;

    	getAI().stopFollow();
    	
    	// Clean up AIs.
		if (_buffTask != null)
			_buffTask.cancel(true);
		
		if (_foodTask != null)
			_foodTask.cancel(true);

		// Clean up actual trained beast.
    	if (_owner != null)
    		_owner.setTrainedBeast(null);
    	
    	// Clean up variables.
    	_buffTask = null;
    	_foodTask = null;
    	_owner = null;
    	_foodSkillId = 0;
    	return true;
    }

    public L2PcInstance getOwner()
    {
    	return _owner;
    }

    public void setOwner(L2PcInstance owner)
    {
    	if (owner != null)
    	{
        	_owner = owner;
	    	setTitle(owner.getName());
	    	setShowSummonAnimation(true);
	    	broadcastPacket(new NpcInfo(this, owner));

	    	owner.setTrainedBeast(this);

	    	// always and automatically follow the owner.
	    	getAI().startFollow(_owner, 200);

	    	// instead of calculating this value each time, let's get this now and pass it on
	    	int totalBuffsAvailable = 0;
    		for (L2Skill skill: getTemplate().getSkills().values())
    		{
    			if (skill.getSkillType() == L2SkillType.BUFF)
    				totalBuffsAvailable++;
    		}

    		// Cancel the buff task, if existing.
    		if (_buffTask != null)
    			_buffTask.cancel(true);
    		
    		// Start the buff task.
    		_buffTask = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new CheckOwnerBuffs(this, totalBuffsAvailable), BUFF_INTERVAL, BUFF_INTERVAL);
    	}
    	// Despawn if no owner
    	else
    		deleteMe();
    }

    /**
     * The "Home" is considered as the central tower in middle of Wild Beast Reserve.
     * @return true or false, depending of the location.
     */
    protected boolean isTooFarFromHome()
    {
    	return !(isInsideRadius(52335, -83086, MAX_DISTANCE_FROM_HOME, true));
    }

	@Override
	public void deleteMe()
    {
		// Clean up AI.
		if (_buffTask != null)
			_buffTask.cancel(true);
		
    	_foodTask.cancel(true);
    	stopHpMpRegeneration();

    	// Clean up actual trained beast.
    	if (_owner != null)
    		_owner.setTrainedBeast(null);
    	
    	// Clean up variables.
    	setTarget(null);
    	_buffTask = null;
    	_foodTask = null;
    	_owner = null;
    	_foodSkillId = 0;

    	// Remove the spawn.
    	super.deleteMe();
    }

    /** 
     * Notification triggered by the owner when the owner is attacked.<br>
     * Tamed mobs will heal/recharge or debuff the enemy according to their skills.
     * @param attacker
     */
    public void onOwnerGotAttacked(L2Character attacker)
    {
    	// Check if the owner is no longer around. If so, despawn.
		if (_owner == null || !_owner.isOnline())
		{
			deleteMe();
			return;
		}
		
		// If the owner is too far away, stop anything else and immediately run towards the owner.
		if (!_owner.isInsideRadius(this, MAX_DISTANCE_FROM_OWNER, true, true))
		{
			getAI().startFollow(_owner, 200);
			return;
		}
		
		// If the owner is dead or if the tamed beast is currently casting a spell,do nothing.
		if (_owner.isDead() || isCastingNow())
			return;
		
		FastMap<Integer, L2Skill> skills = (FastMap<Integer, L2Skill>) getTemplate().getSkills();
		int proba = Rnd.get(3);
		
		// Heal, 25% luck.
		if (proba == 0)
		{
			// Happen only when owner's HPs < 50%
			float HPRatio = ((float) _owner.getCurrentHp()) / _owner.getMaxHp();
			if (HPRatio < 0.5)
			{
				for (L2Skill skill: skills.values())
				{
					switch (skill.getSkillType())
					{
						case HEAL:
						case HOT:
						case BALANCE_LIFE:
						case HEAL_PERCENT:
						case HEAL_STATIC:
							sitCastAndFollow(skill, _owner);
							return;
					}
				}
			}
		}
		// Debuff, 25% luck.
		else if (proba == 1)
		{
			for (L2Skill skill: skills.values())
			{
	    		// if the skill is a debuff, check if the attacker has it already
				if ((skill.getSkillType() == L2SkillType.DEBUFF) && (attacker.getFirstEffect(skill) == null))
					sitCastAndFollow(skill, attacker);
			}
		}
		// Recharge, 25% luck.
		else if (proba == 2)
		{
			// Happen only when owner's MPs < 50%
			float MPRatio = ((float) _owner.getCurrentMp()) / _owner.getMaxMp();
			if (MPRatio < 0.5)
			{
				for (L2Skill skill: skills.values())
				{
					switch (skill.getSkillType())
					{
						case MANAHEAL:
						case MANAHEAL_PERCENT:
						case MANARECHARGE:
						case MPHOT:
							sitCastAndFollow(skill, _owner);
							return;
					}
				}
			}
		}
    }

    /**
     * Prepare and cast a skill:
     * <ul>
     * <li>First, prepare the beast for casting, by abandoning other actions.</li>
     * <li>Next, call doCast in order to cast the spell.</li>
     * <li>Finally, return to auto-following the owner.</li>
     * </ul>
     */
    protected void sitCastAndFollow(L2Skill skill, L2Character target)
    {
		stopMove(null);
		broadcastPacket(new StopMove(this));
		getAI().setIntention(AI_INTENTION_IDLE);

		setTarget(target);
		doCast(skill);
		getAI().setIntention(CtrlIntention.AI_INTENTION_FOLLOW, _owner);
    }

    private static class FoodCheck implements Runnable
    {
    	private L2TamedBeastInstance _tamedBeast;

    	FoodCheck(L2TamedBeastInstance tamedBeast)
    	{
    		_tamedBeast = tamedBeast;
    	}

    	public void run()
    	{
    		// Verify first if the tamed beast is still in the good range. If not, delete it.
    		if (_tamedBeast.isTooFarFromHome())
    		{
    			// After deletion, don't go further.
    			_tamedBeast.deleteMe();
    			return;
    		}
    		
    		// Destroy the food from owner's inventory ; if none is found, delete the pet.
			if (_tamedBeast.getOwner().destroyItemByItemId("BeastMob", _tamedBeast.getFoodType(), 1, _tamedBeast, true))
			{
				_tamedBeast.broadcastPacket(new SocialAction(_tamedBeast, 2));
				_tamedBeast.broadcastPacket(new NpcSay(_tamedBeast.getObjectId(), 0, _tamedBeast.getNpcId(), TAMED_TEXT[Rnd.get(TAMED_TEXT.length)]));
			}
			else
				_tamedBeast.deleteMe();
    	}
    }

    private class CheckOwnerBuffs implements Runnable
    {
    	private L2TamedBeastInstance _tamedBeast;
    	private int _numBuffs;

    	CheckOwnerBuffs(L2TamedBeastInstance tamedBeast, int numBuffs)
    	{
    		_tamedBeast = tamedBeast;
    		_numBuffs = numBuffs;
    	}

    	public void run()
    	{
    		L2PcInstance owner = _tamedBeast.getOwner();

    		// Check if the owner is no longer around. If so, despawn.
    		if (owner == null || !owner.isOnline())
    		{
    			deleteMe();
    			return;
    		}
    		
    		// If the owner is too far away, stop anything else and immediately run towards the owner.
    		if (!isInsideRadius(owner, MAX_DISTANCE_FROM_OWNER, true, true))
    		{
    			getAI().startFollow(owner, 200);
    			return;
    		}
    		
    		// If the owner is dead or if the tamed beast is currently casting a spell,do nothing.
    		if (owner.isDead() || isCastingNow())
    			return;

    		int totalBuffsOnOwner = 0;
    		int i = 0;
    		int rand = Rnd.get(_numBuffs);
    		L2Skill buffToGive = null;

	    	// Get this npc's skills
    		FastMap<Integer, L2Skill> skills = (FastMap<Integer, L2Skill>) _tamedBeast.getTemplate().getSkills();
    		for (L2Skill skill: skills.values())
    		{
    			if (skill.getSkillType() == L2SkillType.BUFF)
    			{
    				if (i == rand)
    					buffToGive = skill;
    				
    				i++;
    				
    				if (owner.getFirstEffect(skill) != null)
    					totalBuffsOnOwner++;
    			}
    		}
    		
			/* If the owner has less than 60% of available buff, cast a random buff.
			 * That buff is casted only if the player hasn't it. */
			if ((_numBuffs * 2 / 3) > totalBuffsOnOwner)
				if (owner.getFirstEffect(buffToGive) == null)
					_tamedBeast.sitCastAndFollow(buffToGive, owner);
			
    		getAI().setIntention(CtrlIntention.AI_INTENTION_FOLLOW, _tamedBeast.getOwner());
    	}
    }
}